#!/usr/bin/env python3
"""
告警分析模块
支持DeepSeek AI分析和内置分析
"""

import requests
import logging
from typing import List, Dict, Any, Tuple
from config import Config

logger = logging.getLogger(__name__)

class AlertAnalyzer:
    """告警分析器 - 支持DeepSeek API和内置分析"""
    
    def __init__(self):
        self.api_config = Config.get_api_config()
        self.ai_engine = self.api_config.get('ai_engine', 'deepseek')
        self.enable_ai = self.api_config.get('enable_ai_analysis', True)
        
        # AI服务配置
        self.deepseek_config = self.api_config.get('deepseek', {})
        self.ollama_config = self.api_config.get('ollama', {})
        
        # 严重程度映射
        self.severity_map = {
            0: "未分类",
            1: "信息",
            2: "警告",
            3: "一般严重",
            4: "严重",
            5: "灾难"
        }
    
    def analyze_alerts(self, alerts: List[Dict[str, Any]]) -> Tuple[str, bool]:
        """
        分析告警 - 自动选择分析方式
        返回: (分析结果, DeepSeek是否成功)
        """
        if not alerts:
            return "✅ **Zabbix系统状态正常**\n\n当前没有未恢复的告警，所有系统运行正常。", True
        
        total_alerts = len(alerts)
        
        # 告警风暴检测和处理
        if total_alerts > 30:
            logger.warning(f"检测到告警风暴：{total_alerts} 个告警")
            return self._handle_alert_storm(alerts)
        
        # 正常告警数量（2-30条）
        if total_alerts >= 2:
            # 如果启用AI分析且配置了AI服务，使用AI分析
            if self.enable_ai:
                if self.ai_engine == 'deepseek' and self.deepseek_config.get('enabled'):
                    logger.info(f"告警数量适中，使用DeepSeek AI进行聚合分析")
                    return self._deepseek_analysis(alerts)
                elif self.ai_engine == 'ollama' and self.ollama_config.get('enabled'):
                    logger.info(f"告警数量适中，使用Ollama进行分析")
                    return self._ollama_analysis(alerts)
                else:
                    logger.info(f"选择的AI引擎 {self.ai_engine} 未启用或未配置，使用内置分析")
                    return self._builtin_analysis(alerts), True
            else:
                logger.info("AI分析已禁用，使用内置分析")
                return self._builtin_analysis(alerts), True
        
        # 单个告警直接使用内置分析
        logger.info("单个告警，使用内置分析")
        return self._builtin_analysis(alerts), True
    
    def _deepseek_analysis(self, alerts: List[Dict[str, Any]]) -> Tuple[str, bool]:
        """使用DeepSeek API分析告警"""
        alert_summary = self._build_alert_summary(alerts)
        
        # 打印发送给DeepSeek的原始数据
        logger.info("\n" + "="*80)
        logger.info("📊 发送给DeepSeek的原始聚合数据:")
        logger.info("\n" + alert_summary)
        logger.info("="*80 + "\n")
        
        prompt = f"""
作为资深运维专家，请对以下Zabbix告警进行初步诊断分析：

{alert_summary}

请提供专业的初步诊断报告，要求：

**输出格式要求**：
- 使用 **粗体** 强调重要内容
- 使用简单的 - 列表格式
- 避免代码块和技术命令
- 章节间用空行分隔

**分析内容**：
1. **问题概述**：简要概括当前告警的整体情况和影响范围
2. **故障分析**：分析每类告警的可能原因和相互关联
3. **影响评估**：评估对业务系统的潜在影响
4. **处理优先级**：按紧急程度排列处理顺序
5. **初步建议**：提供针对性的处理思路和方向

请保持分析的专业性和实用性，重点关注问题的本质和关联性。
"""
        
        try:
            headers = {
                "Content-Type": "application/json",
                "Authorization": f"Bearer {self.deepseek_config.get('api_key')}"
            }
            
            payload = {
                "model": self.deepseek_config.get('model', 'deepseek-chat'),
                "messages": [{"role": "user", "content": prompt}],
                "temperature": 0.7,
                "max_tokens": 2000
            }
            
            response = requests.post(
                self.deepseek_config.get('api_url'),
                headers=headers,
                json=payload,
                timeout=self.deepseek_config.get('timeout', 60)
            )
            
            if response.status_code == 200:
                result = response.json()
                ai_analysis = result['choices'][0]['message']['content']
                
                # 添加运维助手分析引用标识
                formatted_analysis = f"{ai_analysis}\n\n💡 *以上分析由AI运维助手提供，仅供参考*"
                
                logger.info("✅ DeepSeek API分析任务完成")
                return formatted_analysis, True
            else:
                logger.error(f"DeepSeek API请求失败: {response.status_code}")
                return self._builtin_analysis(alerts), False
                
        except Exception as e:
            logger.error(f"DeepSeek API调用异常: {e}")
            return self._builtin_analysis(alerts), False
    
    def _ollama_analysis(self, alerts: List[Dict[str, Any]]) -> Tuple[str, bool]:
        """使用Ollama进行本地AI分析"""
        alert_summary = self._build_alert_summary(alerts)
        
        # 打印发送给Ollama的原始数据
        logger.info("\n" + "="*80)
        logger.info("📊 发送给Ollama的原始聚合数据:")
        logger.info("\n" + alert_summary)
        logger.info("="*80 + "\n")
        
        prompt = f"""
作为资深运维专家，请对以下Zabbix告警进行初步诊断分析：

{alert_summary}

请提供专业的初步诊断报告，要求：

**输出格式要求**：
- 使用 **粗体** 强调重要内容
- 使用简单的 - 列表格式
- 避免代码块和技术命令
- 章节间用空行分隔

**分析内容**：
1. **问题概述**：简要概括当前告警的整体情况和影响范围
2. **故障分析**：分析每类告警的可能原因和相互关联
3. **影响评估**：评估对业务系统的潜在影响
4. **处理优先级**：按紧急程度排列处理顺序
5. **初步建议**：提供针对性的处理思路和方向

请保持分析的专业性和实用性，重点关注问题的本质和关联性。
"""
        
        try:
            payload = {
                "model": self.ollama_config.get('model', 'llama3.1:8b'),
                "prompt": prompt,
                "stream": False
            }
            
            response = requests.post(
                f"{self.ollama_config.get('base_url')}/api/generate",
                json=payload,
                timeout=self.ollama_config.get('timeout', 120)
            )
            
            if response.status_code == 200:
                result = response.json()
                ai_analysis = result.get('response', '分析失败')
                
                # 添加本地AI分析引用标识
                formatted_analysis = f"{ai_analysis}\n\n💡 *以上分析由本地AI助手提供，仅供参考*"
                
                logger.info("✅ Ollama AI分析任务完成")
                return formatted_analysis, True
            else:
                logger.error(f"Ollama API请求失败: {response.status_code}")
                return self._builtin_analysis(alerts), False
                
        except Exception as e:
            logger.error(f"Ollama API调用异常: {e}")
            return self._builtin_analysis(alerts), False
    
    def _build_alert_summary(self, alerts: List[Dict[str, Any]]) -> str:
        """构建告警摘要 - 修复None值问题并增加聚合分析"""
        total_alerts = len(alerts)
        severity_count = {}
        hosts_affected = set()
        
        # 按类型聚合告警
        alert_categories = self._categorize_alerts(alerts)
        
        for alert in alerts:
            # 确保 severity 不为 None
            severity = alert.get('severity')
            if severity is None:
                severity = 0
            else:
                try:
                    severity = int(severity)
                except (ValueError, TypeError):
                    severity = 0
            
            severity_count[severity] = severity_count.get(severity, 0) + 1
            host_name = alert.get('host_name') or '未知主机'
            hosts_affected.add(host_name)
        
        # 构建聚合后的告警摘要
        summary = f"Zabbix告警聚合分析：\n"
        summary += f"总告警数：{total_alerts} 个，影响主机：{len(hosts_affected)} 台\n\n"
        
        # 告警类型聚合汇总（简化版）
        summary += "告警类型汇总：\n"
        for category, items in alert_categories.items():
            hosts_info = self._get_hosts_info(items)
            summary += f"{len(items)} 个 {category}：{hosts_info}\n"
            
            # 提供典型示例（只显示问题描述）
            if len(items) >= 1:
                problem_name = items[0].get('problem_name', '未知告警')
                # 简化问题描述
                if len(problem_name) > 60:
                    problem_name = problem_name[:60] + "..."
                summary += f"  如：{problem_name}\n"
        
        summary += "\n"
        
        # 严重程度统计
        summary += "\n📈 **严重程度分布**\n"
        for severity in sorted(severity_count.keys(), reverse=True):
            if severity_count[severity] > 0:
                count = severity_count[severity]
                severity_name = self.severity_map.get(severity, f"未知({severity})")
                percentage = round(count / total_alerts * 100)
                summary += f"• {severity_name}: {count} 个 ({percentage}%)\n"
        
        # 关键告警详情
        critical_alerts = []
        for alert in alerts:
            severity = alert.get('severity')
            if severity is not None:
                try:
                    severity_int = int(severity)
                    if severity_int >= 4:  # 严重级别以上
                        critical_alerts.append(alert)
                except (ValueError, TypeError):
                    pass
        
        if critical_alerts:
            summary += f"\n重点关注告警（严重级别以上，共{len(critical_alerts)}个）：\n"
            for alert in critical_alerts[:5]:
                severity = alert.get('severity', 0)
                severity_name = self.severity_map.get(severity, f"未知({severity})")
                host_name = alert.get('host_name', '未知主机')
                problem_name = alert.get('problem_name', '未知告警')
                summary += f"  • [{severity_name}] {host_name}: {problem_name}\n"
                if alert.get('trigger_description'):
                    summary += f"    描述: {alert['trigger_description']}\n"
        
        return summary
    
    def _get_hosts_info(self, alerts: List[Dict[str, Any]]) -> str:
        """获取主机信息，智能显示主机名或数量"""
        hosts = set()
        for alert in alerts:
            host_name = alert.get('host_name', '未知主机')
            hosts.add(host_name)
        
        if len(hosts) <= 3:
            # 3台及以下显示具体主机名
            return f"({', '.join(sorted(hosts))})"
        else:
            # 超过3台显示数量
            return f"({len(hosts)} 台主机)"
    
    def _builtin_analysis(self, alerts: List[Dict[str, Any]]) -> str:
        """内置分析（当没有API密钥时）"""
        stats = self._calculate_statistics(alerts)
        
        if stats['total'] == 0:
            return "✅ **系统运行正常**\n\n当前无未恢复告警，所有监控项状态良好。"
        
        # 计算系统健康度
        health_status = self._get_health_status(stats)
        
        report = f"📊 **告警概况**：{stats['total']} 个未恢复告警，涉及 {stats['hosts_count']} 台主机\n\n"
        
        # 告警分类汇总
        alert_categories = self._categorize_alerts(alerts)
        if alert_categories:
            report += "🔍 **告警分类汇总**\n"
            for category, items in alert_categories.items():
                hosts_info = self._get_hosts_info(items)
                report += f"• {category}：{len(items)} 个 {hosts_info}\n"
            report += "\n"
        
        # 严重程度统计
        report += "📈 **严重程度分布**\n"
        for severity in sorted(stats['severity_count'].keys(), reverse=True):
            if stats['severity_count'][severity] > 0:
                count = stats['severity_count'][severity]
                severity_name = self.severity_map.get(severity, f"未知({severity})")
                percentage = round(count / stats['total'] * 100)
                report += f"• {severity_name}: {count} 个 ({percentage}%)\n"
        
        # 重点关注告警
        if stats['critical_alerts']:
            report += f"\n🚨 **高优先级告警** ({len(stats['critical_alerts'])} 个)\n"
            for alert in stats['critical_alerts'][:3]:
                severity_name = self.severity_map.get(alert['severity'])
                report += f"• [{severity_name}] {alert['host_name']}: {alert['problem_name']}\n"
        
        # 处理建议
        report += f"\n🔧 **处理建议**\n"
        suggestions = self._generate_suggestions(stats, alert_categories)
        for suggestion in suggestions:
            report += f"• {suggestion}\n"
        
        # 系统健康度评估
        report += f"\n🎯 **系统健康度**: {health_status}"
        
        return report
    
    def _get_health_status(self, stats: Dict[str, Any]) -> str:
        """计算系统健康度状态"""
        total = stats['total']
        critical_count = len(stats['critical_alerts'])
        
        if critical_count > 0:
            return "🔴 **严重异常** - 存在高风险告警，需立即处理"
        elif total == 0:
            return "🟢 **运行正常** - 所有系统状态良好"
        elif total <= 2:
            return "🟡 **基本正常** - 少量低级别告警，整体可控"
        elif total <= 5:
            return "🟠 **亚健康状态** - 多个低级别异常，建议排查"
        else:
            return "🔴 **异常状态** - 告警数量过多，可能存在系统性问题"
    
    def _categorize_alerts(self, alerts: List[Dict[str, Any]]) -> Dict[str, List[Dict[str, Any]]]:
        """按类型对告警进行分类"""
        categories = {}
        
        for alert in alerts:
            problem_name = alert.get('problem_name', '').lower()
            category = self._classify_alert_type(problem_name)
            
            if category not in categories:
                categories[category] = []
            categories[category].append(alert)
        
        # 按告警数量排序，显示最大的问题类型
        return dict(sorted(categories.items(), key=lambda x: len(x[1]), reverse=True))
    
    def _classify_alert_type(self, problem_name: str) -> str:
        """根据告警名称分类告警类型"""
        problem_name = problem_name.lower()
        
        # 端口/服务相关 - 通用化处理
        if any(keyword in problem_name for keyword in ['port', 'down', 'service', 'connection', 'timeout']):
            return "🚫 服务端口异常"
        
        # 负载相关 - 增加更多关键词
        if any(keyword in problem_name for keyword in ['load average', 'load', 'cpu', 'usage', '负载', 'high', 'too high']):
            return "📈 主机负载告警"
        
        # 内存相关
        if any(keyword in problem_name for keyword in ['memory', 'mem', 'ram', '内存']):
            return "💾 内存告警"
        
        # 磁盘相关
        if any(keyword in problem_name for keyword in ['disk', 'space', 'filesystem', '磁盘', 'storage']):
            return "💽 磁盘空间告警"
        
        # 网络相关
        if any(keyword in problem_name for keyword in ['network', 'ping', 'unreachable', '网络']):
            return "🌐 网络连接告警"
        
        # Agent相关
        if any(keyword in problem_name for keyword in ['agent', 'unavailable']):
            return "🤖 Agent连接告警"
        
        # 数据库相关
        if any(keyword in problem_name for keyword in ['mysql', 'postgresql', 'database', 'db', 'sql']):
            return "🗄️ 数据库告警"
        
        return "❓ 其他告警"
    
    def _generate_suggestions(self, stats: Dict[str, Any], alert_categories: Dict[str, List[Dict[str, Any]]]) -> List[str]:
        """根据告警情况生成针对性建议"""
        suggestions = []
        
        # 高优先级告警处理
        if stats['critical_alerts']:
            suggestions.append("立即处理严重/灾难级别告警")
        
        # 针对不同类型给出具体技术建议
        for category, alerts in alert_categories.items():
            if len(alerts) >= 2:  # 只对数量较多的类型给建议
                if "服务端口" in category:
                    suggestions.append(f"检查 {len(alerts)} 个服务端口：`netstat -tulpn | grep :PORT` 和 `systemctl status SERVICE`")
                elif "负载" in category:
                    suggestions.append(f"排查 {len(alerts)} 台主机负载：`top`、`iotop`、`htop` 检查CPU/IO瓶颈")
                    suggestions.append("分析负载组成：`vmstat 1 5`、`iostat -x 1 5` 查看CPU等待类型")
                elif "Agent" in category:
                    suggestions.append(f"检查 {len(alerts)} 台主机Agent：`systemctl status zabbix-agent` 和网络连通性")
                elif "内存" in category:
                    suggestions.append(f"内存分析 {len(alerts)} 台主机：`free -h`、`ps aux --sort=-%mem` 找内存占用大户")
                elif "磁盘" in category:
                    suggestions.append(f"磁盘清理 {len(alerts)} 台主机：`df -h`、`du -sh /*` 找大文件夹")
                elif "数据库" in category:
                    suggestions.append(f"数据库检查：查看连接数、慢查询日志、表锁情况")
        
        # 根据总告警数量给出系统性建议
        if stats['total'] > 5:
            suggestions.append("排查集群性问题：检查网络、共享存储、负载均衡器状态")
        
        # 增加具体的监控优化建议  
        suggestions.append("检查监控阈值设置，结合业务特点调优告警灵敏度")
        
        return suggestions
    
    def _handle_alert_storm(self, alerts: List[Dict[str, Any]]) -> Tuple[str, bool]:
        """处理告警风暴情况 - 过滤无效告警并生成简化报告"""
        total_alerts = len(alerts)
        
        # 过滤高价值告警
        filtered_alerts = self._filter_high_value_alerts(alerts)
        filtered_count = len(filtered_alerts)
        
        logger.info(f"告警风暴过滤：从 {total_alerts} 个告警中筛选出 {filtered_count} 个高价值告警")
        
        # 生成风暴报告
        storm_report = self._generate_storm_report(alerts, filtered_alerts)
        
        # 如果过滤后的告警仍然过多，不发送给DeepSeek
        if filtered_count > 15:
            logger.warning(f"过滤后仍有 {filtered_count} 个告警，跳过AI分析")
            return storm_report, False
        
        # 如果过滤后告警数量适中，可以发送给DeepSeek
        if filtered_count >= 2 and self.api_key:
            logger.info(f"过滤后 {filtered_count} 个高价值告警，发送给DeepSeek分析")
            ai_analysis, ai_success = self._deepseek_analysis(filtered_alerts)
            
            # 合并风暴报告和AI分析
            combined_report = f"{storm_report}\n\n---\n\n{ai_analysis}"
            return combined_report, ai_success
        
        return storm_report, False
    
    def _filter_high_value_alerts(self, alerts: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
        """过滤出高价值告警，排除常见的噪音告警"""
        high_value_alerts = []
        
        # 优先级排序：严重程度 > 告警类型 > 主机重要性
        for alert in alerts:
            if self._is_high_value_alert(alert):
                high_value_alerts.append(alert)
        
        # 按严重程度排序，只取前15个最重要的
        high_value_alerts.sort(key=lambda x: self._get_alert_priority_score(x), reverse=True)
        return high_value_alerts[:15]
    
    def _is_high_value_alert(self, alert: Dict[str, Any]) -> bool:
        """判断是否为高价值告警"""
        problem_name = alert.get('problem_name', '').lower()
        
        # 排除常见噪音告警
        noise_keywords = [
            'test', 'demo', 'tmp', 'temp', 
            '测试', '演示', '临时',
            'disk space is low on /tmp',
            'swap usage',
            'interface down on'
        ]
        
        for keyword in noise_keywords:
            if keyword in problem_name:
                return False
        
        # 优先保留的高价值告警类型
        high_value_keywords = [
            'down', 'unreachable', 'failed',
            'high load', 'cpu', 'memory',
            'database', 'mysql', 'postgresql',
            'service', 'port', 'timeout',
            '服务', '数据库', '网络', '负载'
        ]
        
        for keyword in high_value_keywords:
            if keyword in problem_name:
                return True
        
        # 检查严重程度
        severity = alert.get('severity')
        if severity is not None:
            try:
                severity_int = int(severity)
                if severity_int >= 4:  # 严重和灾雾级别
                    return True
            except (ValueError, TypeError):
                pass
        
        return False
    
    def _get_alert_priority_score(self, alert: Dict[str, Any]) -> int:
        """计算告警优先级分数"""
        score = 0
        
        # 严重程度分数（最高权重）
        severity = alert.get('severity')
        if severity is not None:
            try:
                severity_int = int(severity)
                score += severity_int * 20  # 灾雾级=100分，严重级=80分
            except (ValueError, TypeError):
                pass
        
        # 告警类型分数
        problem_name = alert.get('problem_name', '').lower()
        if any(k in problem_name for k in ['down', 'unreachable', 'failed']):
            score += 30  # 服务不可用
        elif any(k in problem_name for k in ['database', 'mysql', 'postgresql']):
            score += 25  # 数据库问题
        elif any(k in problem_name for k in ['high load', 'cpu']):
            score += 20  # 性能问题
        elif any(k in problem_name for k in ['memory', 'disk']):
            score += 15  # 资源问题
        
        return score
    
    def _generate_storm_report(self, all_alerts: List[Dict[str, Any]], filtered_alerts: List[Dict[str, Any]]) -> str:
        """生成告警风暴报告"""
        total_count = len(all_alerts)
        filtered_count = len(filtered_alerts)
        
        report = f"🌪️ **告警风暴检测**\n\n"
        report += f"系统检测到大量告警：**{total_count}** 个未恢复告警\n"
        report += f"经过智能过滤，筛选出 **{filtered_count}** 个高价值告警\n\n"
        
        # 统计告警类型分布
        if filtered_alerts:
            alert_categories = self._categorize_alerts(filtered_alerts)
            report += "🔥 **重点关注告警类型**\n"
            for category, items in list(alert_categories.items())[:5]:  # 只显示前5类
                report += f"• {category}：{len(items)} 个\n"
                for item in items[:2]:  # 每类显示2个例子
                    host_name = item.get('host_name', '未知主机')
                    problem_name = item.get('problem_name', '未知告警')
                    report += f"  - {host_name}: {problem_name}\n"
                if len(items) > 2:
                    report += f"  - 及其他 {len(items) - 2} 个相关告警...\n"
            report += "\n"
        
        # 处理建议
        report += "🚨 **紧急处理建议**\n"
        report += "• 立即检查系统整体状态，排查是否存在系统性故障\n"
        report += "• 优先处理上述高价值告警，特别是服务不可用问题\n"
        report += "• 检查监控系统本身是否正常，确认不是误报\n"
        report += "• 考虑临时调高部分告警阈值，降低噪音\n\n"
        
        report += f"📊 **系统健康度**: 🔴 **严重异常** - 告警风暴状态，需立即介入"
        
        return report
    
    def _calculate_statistics(self, alerts: List[Dict[str, Any]]) -> Dict[str, Any]:
        """计算告警统计信息 - 修复None值问题"""
        total_alerts = len(alerts)
        severity_count = {}
        hosts_affected = set()
        critical_alerts = []
        
        for alert in alerts:
            # 处理严重程度
            severity = alert.get('severity')
            if severity is None:
                severity = 0
            else:
                try:
                    severity = int(severity)
                except (ValueError, TypeError):
                    severity = 0
            
            severity_count[severity] = severity_count.get(severity, 0) + 1
            
            # 处理主机名
            host_name = alert.get('host_name') or '未知主机'
            hosts_affected.add(host_name)
            
            # 严重级别以上的告警
            if severity >= 4:
                critical_alerts.append(alert)
        
        return {
            'total': total_alerts,
            'severity_count': severity_count,
            'hosts_count': len(hosts_affected),
            'critical_alerts': critical_alerts
        }